package com.tanjie.blackbean.system.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.tanjie.blackbean.core.web.BlackbeanRequestContextHolder;
import com.tanjie.blackbean.mybatis.query.Condition;
import com.tanjie.blackbean.mybatis.query.PageParam;
import com.tanjie.blackbean.mybatis.support.PageConverter;
import com.tanjie.blackbean.pojo.auth.UserInfo;
import com.tanjie.blackbean.pojo.exception.BizException;
import com.tanjie.blackbean.pojo.response.BizErrorCode;
import com.tanjie.blackbean.system.admin.dao.*;
import com.tanjie.blackbean.system.admin.model.menu.MenuPO;
import com.tanjie.blackbean.system.admin.model.permission.PermissionPO;
import com.tanjie.blackbean.system.admin.model.rel.RoleMenuPO;
import com.tanjie.blackbean.system.admin.model.rel.RolePermissionPO;
import com.tanjie.blackbean.system.admin.model.role.*;
import com.tanjie.blackbean.system.admin.service.IRoleService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 角色表
 *
 * @author tanjie
 * @since 2022-04-20
 */
@Slf4j
@Service
@AllArgsConstructor
public class RoleServiceImpl implements IRoleService {

    RoleMapper roleMapper;

    RoleDaoService sysRoleService;

    RoleMenuMapper roleMenuMapper;

    MenuMapper menuMapper;

    RolePermissionMapper rolePermissionMapper;

    PermissionMapper permissionMapper;

    @Override
    public void create(RoleDTO dto) {
        String orgId = dto.getOrgId();
        if (StrUtil.isBlank(orgId)) {
            orgId = BlackbeanRequestContextHolder.getOrgIdThr();
        }
        verifyCodeRepeat(orgId, dto.getCode().toUpperCase(), null);
        RolePO createOne = BeanUtil.copyProperties(dto, RolePO.class);
        roleMapper.insert(createOne);
    }

    @Override
    public void update(RoleDTO dto) {
        String id = dto.getId();
        RolePO existOne = getByIdThr(id);

        String orgId = dto.getOrgId();
        String modifyCode = dto.getCode();
        String existCode = existOne.getCode();
        if (!existCode.equals(modifyCode)) {
            verifyCodeRepeat(orgId, modifyCode, id);
        }

        RolePO updateOne = BeanUtil.copyProperties(dto, RolePO.class);
        roleMapper.updateById(updateOne);
    }

    @Override
    public void delete(String id) {
        getByIdThr(id);
        roleMapper.deleteById(id);
    }

    @Override
    public RoleVO detail(String id) {
        return BeanUtil.copyProperties(getByIdThr(id), RoleVO.class);
    }

    @Override
    public RolePO getByIdThr(String id) {
        if (StrUtil.isBlank(id)) {
            throw new BizException(BizErrorCode.ID_REQUIRE_NOT_BLANK);
        }
        RolePO existOne = roleMapper.selectById(id);
        if (existOne == null) {
            throw new BizException(BizErrorCode.ID_NOT_EXIST);
        }
        return existOne;
    }

    @Override
    public List<RoleVO> queryList(RoleQuery dto) {
        QueryWrapper<RolePO> queryWrapper = getQueryWrapper(dto);
        List<RolePO> list = roleMapper.selectList(queryWrapper);
        return BeanUtil.copyToList(list, RoleVO.class);
    }

    @Override
    public IPage<RoleVO> queryPage(RoleQuery dto, PageParam pageParam) {
        IPage<RolePO> page = Condition.getPage(pageParam);
        QueryWrapper<RolePO> queryWrapper = getQueryWrapper(dto);
        return PageConverter.convert(roleMapper.selectPage(page, queryWrapper),
                sysRole -> BeanUtil.copyProperties(sysRole, RoleVO.class));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindMenuPermission(RoleBindMenuPermissionDTO dto) {
        // 根据id查询到角色
        String roleId = dto.getRoleId();
        RolePO existRole = getByIdThr(roleId);
        log.info("角色绑定菜单，角色名:{}", existRole.getName());
        // 查询该角色是否绑定过菜单
        List<RoleMenuPO> alreadyBoundMenuList = new LambdaQueryChainWrapper<>(roleMenuMapper).eq(RoleMenuPO::getRoleId, roleId).list();
        List<String> alreadyBoundMenuIdList = alreadyBoundMenuList.stream().map(RoleMenuPO::getMenuId).sorted().collect(Collectors.toList());
        log.info("角色绑定菜单，之前绑定的菜单项:{}", alreadyBoundMenuIdList);
        List<String> modifyMenuIds = dto.getMenuIds();
        log.info("角色绑定菜单，修改的菜单项:{}", modifyMenuIds);
        // 去除请求参数中可能存在的重复项
        modifyMenuIds = modifyMenuIds.stream().distinct().sorted().collect(Collectors.toList());
        // 判断菜单项是否有改动
        boolean isMenuBindChange = !CollUtil.isEqualList(modifyMenuIds, alreadyBoundMenuIdList);
        log.info("角色绑定菜单，菜单项是否有改动:{}", isMenuBindChange);
        if (isMenuBindChange) {
            if (CollectionUtil.isNotEmpty(alreadyBoundMenuList)) {
                // 删除原有的菜单绑定关系
                roleMenuMapper.deleteBatchIds(alreadyBoundMenuList.stream().map(RoleMenuPO::getId).collect(Collectors.toList()));
            }
            if (CollectionUtil.isNotEmpty(modifyMenuIds)) {
                List<RoleMenuPO> roleMenuList = modifyMenuIds.stream().map(menuId -> {
                    MenuPO existMenu = menuMapper.selectById(menuId);
                    if (existMenu == null) {
                        throw new BizException("菜单id：{}在系统中不存在", menuId);
                    }
                    return new RoleMenuPO(roleId, menuId);
                }).collect(Collectors.toList());
                roleMenuList.forEach(rm -> roleMenuMapper.insert(rm));
            }
        }
        // 角色绑定所有菜单权限节点
        List<String> modifyPermissionIdList = dto.getPermissionIds();
        log.info("角色绑定权限，修改的权限项:{}", modifyPermissionIdList);
        List<RolePermissionPO> alreadyBoundPermissions = new LambdaQueryChainWrapper<>(rolePermissionMapper).eq(RolePermissionPO::getRoleId, roleId).list();
        List<String> alreadyBoundPermIdList = alreadyBoundPermissions.stream().map(RolePermissionPO::getPermissionId).sorted().collect(Collectors.toList());
        log.info("角色绑定权限，已经绑定权限项:{}", alreadyBoundPermIdList);
        // 去除权限数组中可能存在的重复项
        modifyPermissionIdList = modifyPermissionIdList.stream().distinct().sorted().collect(Collectors.toList());
        boolean isPermissionBindChange = !CollUtil.isEqualList(modifyPermissionIdList, alreadyBoundPermIdList);
        log.info("角色绑定权限，权限项是否有改动:{}", isPermissionBindChange);
        if (isPermissionBindChange) {
            // 删除已绑定项
            if (CollectionUtil.isNotEmpty(alreadyBoundPermissions)) {
                rolePermissionMapper.deleteBatchIds(alreadyBoundPermissions.stream().map(RolePermissionPO::getId).collect(Collectors.toList()));
            }
            if (CollUtil.isNotEmpty(modifyPermissionIdList)) {
                modifyPermissionIdList.forEach(permId -> {
                    PermissionPO existPerm = permissionMapper.selectById(permId);
                    if (existPerm == null) {
                        throw new BizException("权限id:{}在系统中不存在", permId);
                    }
                    rolePermissionMapper.insert(new RolePermissionPO(roleId, permId));
                });
            }
        }
    }

    @Override
    public List<RoleVO> getOrgRoleList() {
        UserInfo userInfo = BlackbeanRequestContextHolder.getUserInfoThr();
        String orgId = userInfo.getOrgId();
        List<RolePO> poList = new LambdaQueryChainWrapper<>(roleMapper)
                .eq(RolePO::getOrgId, orgId)
                .or()
                .eq(RolePO::getIsPublic, true)
                .orderBy(true, true, RolePO::getSort)
                .list();
        return BeanUtil.copyToList(poList, RoleVO.class);
    }

    private QueryWrapper<RolePO> getQueryWrapper(RoleQuery dto) {
        QueryWrapper<RolePO> queryWrapper = null;
        if (dto != null) {
            queryWrapper = Condition.getQueryWrapper(BeanUtil.copyProperties(dto, RolePO.class));
        }
        return queryWrapper;
    }

    private void verifyCodeRepeat(String orgId, String code, String existId) {
        Long count = new LambdaQueryChainWrapper<>(roleMapper)
                .ne(StrUtil.isNotBlank(existId), RolePO::getId, existId)
                .eq(RolePO::getOrgId, orgId)
                .eq(RolePO::getCode, code).count();
        if (count > 0) {
            throw new BizException("当前组织机构中，角色权限编码:{}已存在", code);
        }
    }

}
